#include <fcntl.h>
#include <pthread.h> // For mutex
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#define LOG_FILE "log.mmap"
#define LOG_FILE_SIZE (10 * 1024 * 1024) // 10 MB

// 日志文件头，必须放在文件的最开始
typedef struct
{
    pthread_mutex_t lock; // 用于多进程/多线程同步的互斥锁
    size_t write_offset;  // 当前写入位置的偏移量
} LogHeader;

LogHeader* header = NULL;
char* log_data_start = NULL;

// 初始化日志系统
int init_logger()
{
    int fd;
    int is_new_file = 0;

    // 1. 打开或创建日志文件
    fd = open(LOG_FILE, O_RDWR | O_CREAT, 0666);
    if (fd == -1)
    {
        perror("open");
        return -1;
    }

    // 检查文件是否是新创建的
    struct stat file_info;
    if (fstat(fd, &file_info) == -1)
    {
        perror("fstat");
        close(fd);
        return -1;
    }

    if (file_info.st_size == 0)
    {
        is_new_file = 1;
        // 2. 如果是新文件，扩展文件到指定大小
        if (ftruncate(fd, LOG_FILE_SIZE) == -1)
        {
            perror("ftruncate");
            close(fd);
            return -1;
        }
    }

    // 3. 将整个文件映射到内存
    void* mapped_region = mmap(NULL, LOG_FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped_region == MAP_FAILED)
    {
        perror("mmap");
        close(fd);
        return -1;
    }
    close(fd); // 文件描述符可以关闭了

    header = (LogHeader*)mapped_region;
    log_data_start = (char*)mapped_region + sizeof(LogHeader);

    // 4. 如果是新文件，需要初始化互斥锁
    if (is_new_file)
    {
        header->write_offset = 0;

        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        // 设置为 PTHREAD_PROCESS_SHARED 使其能在多进程间共享
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&header->lock, &attr);
        pthread_mutexattr_destroy(&attr);

        printf("Logger initialized. New log file created.\n");
    }
    else
    {
        printf("Logger initialized. Opened existing log file.\n");
    }

    return 0;
}

// 写入日志
void write_log(const char* message)
{
    if (!header)
    {
        fprintf(stderr, "Logger not initialized.\n");
        return;
    }

    // 加锁以保证线程/进程安全
    pthread_mutex_lock(&header->lock);

    size_t msg_len = strlen(message);
    size_t available_space = (LOG_FILE_SIZE - sizeof(LogHeader)) - header->write_offset;

    // 检查空间是否足够，如果不够，可以从头开始写（循环日志）
    if (msg_len + 1 > available_space)
    {
        printf("Log file full, wrapping around.\n");
        header->write_offset = 0;
    }

    // 写入日志内容
    char* write_ptr = log_data_start + header->write_offset;
    memcpy(write_ptr, message, msg_len);
    write_ptr[msg_len] = '\n'; // 添加换行符

    // 更新偏移量
    header->write_offset += (msg_len + 1);

    // 解锁
    pthread_mutex_unlock(&header->lock);
}

// 强制同步到磁盘
void flush_log()
{
    if (header)
    {
        printf("Flushing logs to disk...\n");
        if (msync(header, LOG_FILE_SIZE, MS_SYNC) == -1)
        {
            perror("msync");
        }
    }
}

// 清理和关闭
void shutdown_logger()
{
    if (header)
    {
        flush_log(); // 程序退出前确保所有日志都写入磁盘
        munmap(header, LOG_FILE_SIZE);
        header = NULL;
    }
}

int main()
{
    if (init_logger() != 0)
    {
        return 1;
    }

    // 模拟写入日志
    write_log("This is the first log entry.");
    write_log("Application starting up...");

    // 模拟多进程写入 (fork)
    pid_t pid = fork();
    if (pid == 0)
    { // 子进程
        write_log("[CHILD] Child process logging.");
        exit(0);
    }
    else
    {               // 父进程
        wait(NULL); // 等待子进程结束
        write_log("[PARENT] Parent process logging after child.");
    }

    printf("Current log file size (offset): %zu\n", header->write_offset);
    printf("Check the content of '%s' file.\n", LOG_FILE);

    shutdown_logger();

    return 0;
}
